package com.hwtx.form.domain.handle.datamodel;

import com.google.common.collect.Lists;
import com.hwtx.form.annotation.Action;
import com.hwtx.form.annotation.Log;
import com.hwtx.form.annotation.OperationType;
import com.hwtx.form.domain.def.Select;
import com.hwtx.form.domain.def.meta.*;
import com.hwtx.form.domain.ds.DataSourceContextHolder;
import com.hwtx.form.domain.repo.DataModelRepo;
import com.hwtx.form.domain.vo.DataModelDetailQuery;
import com.hwtx.form.domain.vo.ResultVo;
import com.hwtx.form.persistence.DataSourceRepo;
import com.hwtx.form.persistence.SQLType;
import com.hwtx.form.util.*;
import io.geekidea.boot.config.properties.BootProperties;
import io.geekidea.boot.framework.exception.BusinessException;
import io.geekidea.boot.framework.page.Paging;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hwtx.form.domain.FormConstants.*;
import static com.hwtx.form.persistence.DataSourceRepo.getSqlAndParam;
import static com.hwtx.form.util.FormUtil.*;
import static com.hwtx.form.util.QueryParserResult.NO_NEED_TABLE;

@Slf4j
@org.springframework.stereotype.Component
public class DataModelAction {

    public static final String ONLY_TABLE = "onlyTable";
    @Resource
    DbSynService dbSynService;
    @Resource
    JdbcTemplate jdbcTemplate;
    @Resource
    private DataSourceRepo dataSourceRepo;
    @Resource
    private DataModelRepo dataModelRepo;
    @Resource
    private BootProperties bootProperties;
    @Resource
    private ServerProperties webApplicationContext;

    @NotNull
    private static List<Select.Option> getTablesWithNumOptions(DataModel dataModel, List<DataModelRelate> relates, boolean onlyTable) {
        Set<String> contained = new HashSet<>();
        List<Select.Option> options = Lists.newArrayList();
        Select.Option option = new Select.Option();
        option.setLabel(onlyTable ? dataModel.getMaster() : dataModel.getMaster() + "[0]");
        option.setValue(dataModel.getMaster());
        options.add(option);
        contained.add(dataModel.getMaster());
        int i = 1;
        for (DataModelRelate relate : relates) {
            if (!contained.contains(relate.getFirstName())) {
                option = new Select.Option();
                option.setLabel(onlyTable ? relate.getFirstName() : relate.getFirstName() + "[" + i++ + "]");
                option.setValue(relate.getFirstName());
                options.add(option);
                contained.add(relate.getFirstName());
            }
            if (!contained.contains(relate.getSecondName())) {
                option = new Select.Option();
                option.setLabel(onlyTable ? relate.getSecondName() : relate.getSecondName() + "[" + i++ + "]");
                option.setValue(relate.getSecondName());
                options.add(option);
                contained.add(relate.getSecondName());
            }
        }
        return options;
    }
    @Action
    @Log(type = OperationType.GET, value = "获取sql测试数据")
    public Paging<?> test(Map<String, Object> formData) {
        String sql = getStrPostValue(formData, "sql");
        if (StringUtils.isBlank(sql)) {
            throw new BusinessException("sql不能为空");
        }
        QueryParserResult result;
        try {
            result = QueryParser.parser(sql);
        } catch (JSQLParserException e) {
            throw new BusinessException("sql解析失败");
        }
        Map<String, Collection<Object>> params = convertToCollectionMap(formData);
        params.remove("sql");
        Pair<String, List<Object>> sqlAndParam = getSqlAndParam(result, List.of(params), 1000, "and");
        if (sqlAndParam == null) {
            return new Paging<>();
        }
        Long pageSize = getLongPostValue(formData, "pageSize");
        Long pageNum = getLongPostValue(formData, "pageIndex");
        if (pageSize == null || pageNum == null) {
            pageSize = 10L;
            pageNum = 1L;
        }
        Long start = (pageNum - 1) * pageSize;
        List<Object> param = sqlAndParam.getRight();
        sql = sqlAndParam.getLeft();
        param.add(pageSize);
        param.add(start);
        List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql + " LIMIT ? OFFSET ?", param.toArray());
        param.removeLast();
        param.removeLast();
        String countSql;
        Paging<Map<String, Object>> paging = new Paging<>();
        if (sql.contains("group by") || sql.contains("GROUP BY")) {
            countSql = "SELECT count(*) FROM (" + sql + ") t";
        } else {
            int index = sql.indexOf("FROM");
            countSql = "SELECT count(*) " + sql.substring(index);
        }
        Long count = jdbcTemplate.queryForObject(countSql, Long.class, param.toArray());
        paging.setItems(rows);
        paging.setTotal(count);
        return paging;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取sql模型测试表单")
    public String testSqlForm(Map<String, Object> formData) throws IOException {

//        String json = FileUtils.readFileToString(new File(getClass().getClassLoader().getResource("1.json").getFile()));
//        return json;

        String sql = getStrPostValue(formData, "sqlContent");
        if (StringUtils.isBlank(sql)) {
            throw new BusinessException("sql不能为空");
        }
        String contextPath = getUrlContext(webApplicationContext);
        Component.Form form = new Component.Form();
        form.setId("testSqlForm");
        form.setCanAccessSuperData(false);
        List<Component> body = Lists.newArrayList();
        Component.TextArea sqlInput = new Component.TextArea();
        sqlInput.setLabel("sql");
        sqlInput.setName("sql");
        sqlInput.setHorizontalMode();
        sqlInput.setLabelWidth("var(--sizes-base-20)");
        sqlInput.setValue(sql);
        sqlInput.setDisabled(true);
        body.add(sqlInput);
        Component.Hidden hidden = new Component.Hidden();
        hidden.setName("loadData");
        hidden.setId("loadData");
        hidden.setValue("0");
        body.add(hidden);
        String modelName = getStrPostValue(formData, "modelName");
        List<String> params = Lists.newArrayList();
        if (StringUtils.isNotEmpty(sql) && sql.contains("@")) {
            try {
                QueryParserResult result = QueryParser.parser(sql);
                result.getVars().forEach((table, varInfos) -> {
                    if (!table.equals(NO_NEED_TABLE)) {
                        Map<String, com.hwtx.form.persistence.ds.metadata.Column> columns = dataSourceRepo.getColumns(table);
                        varInfos.stream().collect(Collectors.groupingBy(QueryParserResult.VarInfo::getColumn)).forEach((name, vars) -> {
                            com.hwtx.form.persistence.ds.metadata.Column column = columns.get(name);
                            Component.Input input = null;
                            SQLType type = SQLType.getType(column.getTypeName());
                            for (QueryParserResult.VarInfo varInfo : vars) {
                                if (SQLType.isString(type)) {
                                    input = Component.Input.getText(varInfo.getVarName(), varInfo.getVarName(), "");
                                } else if (SQLType.isDatetime(type)) {
                                    input = Component.Input.getDataTime(varInfo.getVarName(), varInfo.getVarName(), "");
                                } else if (SQLType.isNumber(type)) {
                                    input = Component.Input.getNumber(varInfo.getVarName(), varInfo.getVarName(), "");
                                }
                                if (input != null) {
                                    input.setHorizontalMode();
                                    input.setRequired(true);
                                    input.setLabelWidth("var(--sizes-base-20)");
                                    params.add(input.getName());
                                    body.add(input);
                                }
                            }
                        });
                    } else {
                        varInfos.forEach(varInfo -> {
                            if (varInfo.getType() != null) {
                                if (Types.isNumber(varInfo.getType())) {
                                    body.add(Component.Input.getNumber(varInfo.getVarName(), varInfo.getVarName(), varInfo.getColumn()));
                                } else if (Types.isDatetime(varInfo.getType())) {
                                    body.add(Component.Input.getDataTime(varInfo.getVarName(), varInfo.getVarName(), varInfo.getColumn()));
                                } else {
                                    body.add(Component.Input.getText(varInfo.getVarName(), varInfo.getVarName(), varInfo.getColumn()));
                                }
                            }
                        });
                    }
                });
            } catch (JSQLParserException e) {
                log.error("sql解析失败", e);
                throw new BusinessException("sql解析失败");
            }
        }

        Crud crud = new Crud();
        Component.OnEvent onEvent = new Component.OnEvent();
        Map<String, Map<String, List<Component.Action>>> actionsMap = new HashMap<>();
        actionsMap.put("loadDataEvent", new HashMap<>(Map.of("actions", List.of(Component.Action.getReloadAction("testCrud", null)))));
        onEvent.setActionsMap(actionsMap);
        crud.setOnEvent(onEvent);
        Button load = new Button();
        load.setLabel("加载");
        onEvent = new Component.OnEvent();
        Component.ClickEvent clickEvent = new Component.ClickEvent();
        clickEvent.setActions(List.of(
                Component.Action.getValidateAction("testSqlForm", "form_validate_result"),
                Component.Action.getStopPropagationAction("${!ISEMPTY(form_validate_result.error)}"),
                Component.Action.getSetValueAction("1", "loadData"),
                Component.Action.getBroadcastAction("loadDataEvent")));
        onEvent.setClick(clickEvent);
        load.setOnEvent(onEvent);
        crud.setHeaderToolbar(List.of(load));
        crud.setId("testCrud");
        crud.setSyncLocation(false);
        Map<String, Object> data = new HashMap<>(Map.of("&", "$$", "sql", sql));
        params.forEach(param -> data.put(param, "${" + param + "}"));
        crud.setApi(Api.builder().url(bootProperties.getDomain() + contextPath + "/form/app/dataModel/test")
                .data(data).method(HTTP_POST).sendOn("this.loadData>0").build());
        crud.setColumns(dataModelRepo.getDetails(DataModelDetailQuery.builder().modelName(modelName).build()).stream().map(detail -> {
            Column column = new Column();
            column.setName(StringUtils.isNotEmpty(detail.getDescription()) ? detail.getDescription() : detail.getName());
            column.setLabel(column.getName());
            Style style = new Style();
            style.addBorderRight();
            column.setStyle(style);
            return column;
        }).toList());
        if (crud.getColumns().isEmpty()) {
            throw new BusinessException("数据模型没有字段");
        }
        body.add(crud);
        form.setBody(body);
        return JacksonUtil.object2json(form);
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "同步数据库到数据模型")
    public boolean syn(Map<String, Object> formData) {
        Long dataModelId = getLongPostValue(formData, "id");
        if (dataModelId == null) {
            throw new BusinessException("数据模型ID不能为空");
        }
        DataModel dataModel = dataModelRepo.getDataModel(dataModelId);
        if (dataModel == null) {
            throw new BusinessException("数据模型不存在,ID = " + dataModelId);
        }
        List<String> handledTables = Lists.newArrayList();
        List<DataModelDetail> existMasterDetails = dataModelRepo.getDetails(DataModelDetailQuery.builder().modelName(dataModel.getModelName()).build());
        Map<String, List<DataModelDetail>> existDbDetails = existMasterDetails.stream().collect(Collectors.groupingBy(DataModelDetail::getTableName));
        List<DataModelDetail> details = existDbDetails.get(dataModel.getMaster());
        Map<String, DataModelDetail> tableDetails = details.stream().collect(Collectors.toMap(DataModelDetail::getName, Function.identity()));
        compareAndSynDetails(dataModel.getMaster(), dataModel.getModelName(), dataModel.getDs(), tableDetails);
        log.info("数据模型同步完成,表 = {}", dataModel.getMaster());
        handledTables.add(dataModel.getMaster());
        if (DataModel.isAggTableModel(dataModel.getTableType())) {
            List<DataModelRelate> relates = dataModelRepo.getRelates(dataModel.getModelName());
            if (CollectionUtils.isEmpty(relates)) {
                return true;
            }
            relates.forEach(relate -> {
                if (!handledTables.contains(relate.getSecondName())) {
                    List<DataModelDetail> ddetails = existDbDetails.get(relate.getSecondName());
                    if (ddetails == null) {
                        return;
                    }
                    Map<String, DataModelDetail> ddetailsMap = ddetails.stream().collect(Collectors.toMap(DataModelDetail::getName, Function.identity()));
                    compareAndSynDetails(relate.getSecondName(), dataModel.getModelName(), dataModel.getDs(), ddetailsMap);
                    log.info("数据模型同步完成,表 = {}", relate.getSecondName());
                    handledTables.add(relate.getSecondName());
                }
            });
        }
        return true;
    }

    private void compareAndSynDetails(String table, String modelName, String ds, Map<String, DataModelDetail> existDbDetails) {
        List<DataModelDetail> details = DataModel.dataModelDetailsFromDb(table, modelName, ds, dataSourceRepo);
        Map<String, DataModelDetail> dbDetails = details.stream().collect(Collectors.toMap(DataModelDetail::getName, Function.identity()));
        List<DataModelDetail> addDetails = Lists.newArrayList();
        List<DataModelDetail> updateDetails = Lists.newArrayList();
        List<DataModelDetail> delDetails = Lists.newArrayList();
        dbDetails.forEach((key, value) -> {
            DataModelDetail dataModelDetail = existDbDetails.get(key);
            if (dataModelDetail == null) {
                addDetails.add(value);
            } else {
                if (!equal(value, dataModelDetail)) {
                    value.setId(dataModelDetail.getId());
                    updateDetails.add(value);
                }
            }
        });
        existDbDetails.forEach((key, value) -> {
            DataModelDetail dataModelDetail = dbDetails.get(key);
            if (dataModelDetail == null) {
                delDetails.add(value);
            }
        });
        if (CollectionUtils.isNotEmpty(addDetails)) {
            dataModelRepo.saveDetails(addDetails);
        }
        if (CollectionUtils.isNotEmpty(updateDetails)) {
            dataModelRepo.updateDetails(updateDetails);
        }
        if (CollectionUtils.isNotEmpty(delDetails)) {
            delDetails.forEach(dataModelDetail -> dataModelRepo.deleteDetails(dataModelDetail.getId()));
        }
    }

    private boolean equal(DataModelDetail d1, DataModelDetail d2) {
        if (!Objects.equals(d1.getName(), d2.getName())) {
            return false;
        }
        if (!Objects.equals(d1.getType(), d2.getType())) {
            return false;
        }
        if (!Objects.equals(d1.getLength(), d2.getLength())) {
            return false;
        }
        if (!Objects.equals(d1.getPoints(), d2.getPoints())) {
            return false;
        }
        if (!Objects.equals(d1.getNotNull(), d2.getNotNull())) {
            return false;
        }
        if (!Objects.equals(d1.getPriKey(), d2.getPriKey())) {
            return false;
        }
        if (!Objects.equals(d1.getIsUnique(), d2.getIsUnique())) {
            return false;
        }
        if (!Objects.equals(d1.getDefValue(), d2.getDefValue())) {
            return false;
        }
        return Objects.equals(d1.getDescription(), d2.getDescription());
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "初始化模型")
    public boolean initDb(Map<String, Object> formData) {
        if (formData.get("modelName") == null || StringUtils.isBlank(formData.get("modelName").toString())) {
            throw new BusinessException("数据模型名称不能为空");
        }
        if (formData.get("master") == null || StringUtils.isBlank(formData.get("master").toString())) {
            throw new BusinessException("模型主表不能为空");
        }
        String modelName = formData.get("modelName").toString();
        if (dataModelRepo.getDataModel(modelName) == null) {
            throw new BusinessException("数据模型不存在, 无法初始化");
        }

        DEFAULT_FIELDS.entrySet().stream().filter(entry -> !dataModelRepo.hasFields(modelName, new Object[]{entry.getKey()})).forEach(entry -> {
            entry.getValue().setModelName(modelName);
            entry.getValue().setTableName(formData.get("master").toString());
            entry.getValue().setStatus(Status.VALID.getCode());
            dataModelRepo.saveDetail(entry.getValue());
            entry.getValue().setId(null);
        });
        return true;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取建立关系中的主表")
    public Select relateTables(Map<String, Object> formData) {
        if (formData.get("modelName") == null || formData.get("modelName").toString().isEmpty()) {
            throw new BusinessException("数据模型名称不能为空");
        }
        String modelName = formData.get("modelName").toString();
        DataModel dataModel = dataModelRepo.getDataModel(modelName);
        if (dataModel == null) {
            throw new BusinessException("数据模型不存在,需先保存模型定义");
        }
        Select select = new Select();
        List<DataModelRelate> relates = dataModelRepo.getRelates(modelName);
        Boolean onlyTable = FormUtil.getBooleanValue(formData, ONLY_TABLE);
        List<Select.Option> options = getTablesWithNumOptions(dataModel, relates, onlyTable != null && onlyTable);
        select.setOptions(options);
        return select;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取数据源中的表")
    public Select tables(Map<String, Object> formData) {
        if (formData.get("ds") == null || formData.get("ds").toString().isEmpty()) {
            throw new BusinessException("数据源不能为空");
        }
        String ds = formData.get("ds").toString();
        Select select = new Select();
        Map<String, String> tables = dataSourceRepo.getTableAndComments();
        String key = DataSourceContextHolder.getDataSourceKey();
        DataSourceContextHolder.setDefaultDsKey();
        List<Select.Option> options = new ArrayList<>(tables.entrySet().stream().map(table -> {
            Select.Option option = new Select.Option();
            option.setLabel(table.getKey());
            option.setValue(table.getKey());
            option.setComment(table.getValue());
            return option;
        }).toList());
        if (formData.get("modelName") != null && !formData.get("modelName").toString().isEmpty()) {
            DataModel model = dataModelRepo.getDataModel(formData.get("modelName").toString());
            if (model != null && Objects.equals(model.getDs(), ds)) {
                String master = model.getMaster();
                Select.Option option = new Select.Option();
                option.setLabel(master);
                option.setValue(master);
                options.add(option);
            }
        }
        DataSourceContextHolder.setDataSourceKey(key);
        select.setOptions(options);
        return select;
    }

    @Action
    @Log(type = OperationType.GET, value = "数据源名称是否存在")
    public ResultVo existName(Map<String, Object> formData) {
        if (formData.get("name") != null && !formData.get("name").toString().isBlank()) {
            DataModel dataModel = dataModelRepo.getDataModel(formData.get("name").toString());
            Long id = null;
            if (formData.get("id") != null && !formData.get("id").toString().isBlank()) {
                id = Long.valueOf(formData.get("id").toString());
            }
            if (dataModel != null && !Objects.equals(dataModel.getId(), id)) {
                return ResultVo.builder().success(true).data(true).msg("数据模型名【" + formData.get("name") + "】已存在").build();
            }
        }
        return ResultVo.builder().success(true).data(false).build();
    }

    @Action
    @Log(type = OperationType.GET, value = "名称是否存在")
    public ResultVo masterChange(Map<String, Object> formData) {
        if (formData.get("modelName") != null && !formData.get("modelName").toString().isBlank() && formData.get("master") != null && !formData.get("master").toString().isBlank()) {
            DataModel dataModel = dataModelRepo.getDataModel(formData.get("modelName").toString());

            if (dataModel != null && !Objects.equals(dataModel.getMaster(), formData.get("master").toString())) {
                return ResultVo.builder().success(true).data(true).build();
            }
        }
        return ResultVo.builder().success(true).data(false).build();
    }

    @Action
    @Log(type = OperationType.GET, value = "获取表字段名")
    public Select fields(Map<String, Object> formData) {
        if (formData.get("modelName") == null) {
            throw new BusinessException("数据模型名称不能为空");
        }
        String table = formData.get("table").toString();
        if (StringUtils.isBlank(table)) {
            throw new BusinessException("表名不能为空");
        }
        Select select = new Select();
        List<Select.Option> options = new ArrayList<>();
        dataSourceRepo.getColumns(table).values().forEach(column -> {
            Select.Option option = new Select.Option();
            option.setLabel(column.getName());
            option.setValue(column.getName());
            options.add(option);
        });
        select.setOptions(options);
        return select;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取模型字段")
    @Deprecated
    //fixme 模型涉及多个字段
    public Select modelFields(Map<String, Object> formData) {
        String modelName = getStrPostValue(formData, "modelName");
        if (modelName == null) {
            throw new BusinessException("数据模型名称不能为空");
        }
        Select select = new Select();
        List<Select.Option> options = new ArrayList<>();
        dataModelRepo.getDetails(DataModelDetailQuery.builder().modelName(modelName).build()).forEach(detail -> {
            Select.Option option = new Select.Option();
            option.setLabel(detail.getName());
            option.setValue(detail.getName());
            options.add(option);
        });
        select.setOptions(options);
        return select;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取模型表字段")
    //fixme 模型涉及多个字段
    public Select modelTableFields(Map<String, Object> formData) {
        String modelName = getStrPostValue(formData, "modelName");
        if (modelName == null) {
            throw new BusinessException("数据模型名称不能为空");
        }
        String table = getStrPostValue(formData, "table");
        if (table == null) {
            DataModel dataModel = dataModelRepo.getDataModel(modelName);
            if (dataModel == null) {
                throw new BusinessException("数据模型不存在");
            }
            table = dataModel.getMaster();
        }
        Select select = new Select();
        List<Select.Option> options = new ArrayList<>();
        dataModelRepo.getDetails(DataModelDetailQuery.builder().modelName(modelName).tableName(table).build()).forEach(detail -> {
            Select.Option option = new Select.Option();
            option.setLabel(detail.getName());
            option.setValue(detail.getName());
            options.add(option);
        });
        select.setOptions(options);
        return select;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取全部表字段名")
    @Deprecated
    public Select allFields(Map<String, Object> formData) {
        if (formData.get("modelName") == null) {
            throw new BusinessException("数据模型名称不能为空");
        }
        String modelName = formData.get("modelName").toString();
        List<DataModelRelate> relates = dataModelRepo.getRelates(modelName);
        DataModel dataModel = dataModelRepo.getDataModel(modelName);
        Set<String> contained = new HashSet<>();
        List<Select.Option> options = new ArrayList<>();
        dataSourceRepo.getColumns(dataModel.getMaster()).values().forEach(column -> {
            Select.Option option = new Select.Option();
            option.setLabel(dataModel.getMaster() + "[0]" + "." + column.getName());
            option.setValue(dataModel.getMaster() + "." + column.getName());
            options.add(option);
        });
        contained.add(dataModel.getMaster());
        final AtomicInteger i = new AtomicInteger(1);
        for (DataModelRelate relate : relates) {
            if (!contained.contains(relate.getFirstName())) {
                dataSourceRepo.getColumns(dataModel.getMaster()).values().forEach(column -> {
                    Select.Option option = new Select.Option();
                    option.setLabel(relate.getFirstName() + "[" + i.get() + "]" + "." + column.getName());
                    option.setValue(relate.getFirstName() + "." + column.getName());
                    options.add(option);
                });
                i.getAndDecrement();
            }
            if (!contained.contains(relate.getSecondName())) {
                dataSourceRepo.getColumns(dataModel.getMaster()).values().forEach(column -> {
                    Select.Option option = new Select.Option();
                    option.setLabel(relate.getSecondName() + "[" + i.get() + "]" + "." + column.getName());
                    option.setValue(relate.getSecondName() + "." + column.getName());
                    options.add(option);
                });
                i.getAndDecrement();
            }
        }
        Select select = new Select();
        select.setOptions(options);
        return select;
    }

    @Action
    @Log(type = OperationType.GET, value = "获取数据表及其下字段")
    @Deprecated
    public Select.NestedSelect tableAndFields(Map<String, Object> formData) {
        String table1 = formData.get("firstName").toString();
        String table2 = formData.get("secondName").toString();
        if (StringUtils.isBlank(table1) || StringUtils.isBlank(table2)) {
            throw new BusinessException("表名不能为空");
        }
        Select.NestedSelect select = new Select.NestedSelect();
        List<Select.NestedOption> options = new ArrayList<>();
        Select.NestedOption parent = new Select.NestedOption();
        buildTableFields(table1, options, parent);
        parent = new Select.NestedOption();
        buildTableFields(table2, options, parent);
        select.setOptions(options);
        return select;
    }

    private void buildTableFields(String table, List<Select.NestedOption> options, Select.NestedOption parent) {
        parent.setLabel(table);
        parent.setValue(table);
        parent.setChildren(dataSourceRepo.getColumns(table).values().stream().map(column -> {
            Select.NestedOption option = new Select.NestedOption();
            option.setLabel(column.getName());
            option.setValue(table + "." + column.getName());
            return option;
        }).toList());
        options.add(parent);
    }

    @Action
    @Log(type = OperationType.UPDATE, value = "同步数据模型到数据库")
    @Transactional
    public ResultVo sync(Map<String, Object> formData) {
        if (formData.get("modelName") == null) {
            return ResultVo.builder().msg("数据模型名称不能为空").success(false).build();
        }
        return dbSynService.sync(FormUtil.getStrPostValue(formData, "modelName"), Boolean.TRUE.equals(FormUtil.getBooleanValue(formData, "delItem")));
    }
}
